#include <tcpserver.h>

TCPServer::TCPServer(std::function<void(int, std::string)> onError) : BaseSocket(onError, TCP)
{
    int opt = 1;
    setsockopt(this->sock,SOL_SOCKET,SO_REUSEADDR,(char*)&opt,sizeof(int));
#ifndef _WIN32
    setsockopt(this->sock,SOL_SOCKET,SO_REUSEPORT,(char*)&opt,sizeof(int));
#endif // !_WIN32

}

void TCPServer::Bind(int port, std::function<void(int, std::string)> onError)
{
    this->Bind("0.0.0.0", port, onError);
}

void TCPServer::Bind(const char* address, uint16_t port, std::function<void(int, std::string)> onError)
{
    if (inet_pton(AF_INET, address, &this->address.sin_addr) <= 0)
    {
        onError(errno, "Invalid address. Address type not supported.");
        return;
    }
        this->address.sin_family = AF_INET;
        this->address.sin_port = htons(port);

        if (bind(this->sock, (const sockaddr*)&this->address, sizeof(this->address)) < 0)
        {
            onError(errno, "Cannot bind the socket.");
            return;
        }
   
}
void TCPServer::Listen(std::function<void(int, std::string)> onError)
{
    if (listen(this->sock, SOMAXCONN/*10*/) < 0)
    {
        onError(errno, "Error: Server can't listen the socket.");
        return;
    }

    std::thread acceptThread(Accept, this, onError);
    acceptThread.detach();
}

void TCPServer::Close()
{
#ifdef _WIN32
    shutdown(this->sock, SD_BOTH);
#else
    shutdown(this->sock, SHUT_RDWR);
#endif // _WIN32

    BaseSocket::Close();
}

void TCPServer::Accept(TCPServer *server, std::function<void(int, std::string)> onError)
{
    sockaddr_in newSocketInfo;
    socklen_t newSocketInfoLength = sizeof(newSocketInfo);

    int newSock;
    while (!server->isClosed)
    {
        while ((newSock = accept(server->sock, (sockaddr *)&newSocketInfo, &newSocketInfoLength)) < 0)
        {
            if (errno == EBADF || errno == EINVAL) return;

            onError(errno, "Error while accepting a new connection.");
            return;
        }

        if (!server->isClosed && newSock >= 0)
        {
            TCPSocket *newSocket = new TCPSocket([](int e, std::string er) { FDR_UNUSED(e); FDR_UNUSED(er); }, newSock);
            newSocket->setAddressStruct(newSocketInfo);

            server->onNewConnection(newSocket);
            newSocket->Listen();
        }
    }
}
